﻿using System;
using System.Collections.Generic;
using System.Configuration;
using System.Data.Objects;
using System.Diagnostics;
using System.Linq;
using Citi.Helpers;
using Citi.Scheduler.Domain;
using Citi.Scheduler.Domain.Utils;
using System.IO;
using System.ServiceProcess;
using Citi.Scheduler.WindowsServices.EmailSenderWcf;
using System.Threading;

namespace Citi.Scheduler.WindowsServices
{
    public static class MonitorAction
    {
        private enum EmailReceiverType
        {
            To, Cc, Bcc
        }

        private static Application SchedulerApplication
        {
            get { return new Application().SelectAll<Application>().First(x => x.FullName == "Citi Scheduler"); }
        }

        public static bool ProcessRunning { get; set; }
        public static EventLog EventLog { get; set; }
        private static string NetworkUser { get { return ConfigurationManager.AppSettings["NetworkUser"]; } }
        private static string Domain { get { return NetworkUser.Split('\\')[0]; } }
        private static string UserWithoutDomain { get { return NetworkUser.Split('\\')[1]; } }
        private static string NetWorkPassword { get { return ConfigurationManager.AppSettings["NetWorkPassword"]; } }
        private static string WebUrl { get { return ConfigurationManager.AppSettings["WebUrl"]; } }
        private static string TempFolder { get { return ConfigurationManager.AppSettings["TempFolder"]; } }
        private const string ServiceExecutionName = "Citi Scheduler Execution Service";
        private const string ServiceMonitorName = "Citi Scheduler Monitor Service";
        private const string ServiceSchedulerName = "Citi Scheduler Service";
        private static string DateConsoleWrite { get { return DateTime.Now.ToString("dd/MM/yyy HH:mm:ss"); } }
        private static DateTime lastEmailSentServicesStopped;

        internal static string ErrorLog
        {
            get
            {
                var dllFolder = Path.GetDirectoryName(TempFolder);
                return Path.Combine(dllFolder, "ErrorLog.txt");
            }
        }

        public static void StartEngine()
        {
            if (ProcessRunning) return;

            try
            {
                ProcessRunning = true;

                SetOverwriteChanges(Context.Instance.RoutineExecutions);
                SetOverwriteChanges(Context.Instance.Routines);
                SetOverwriteChanges(Context.Instance.RoutineErrorEmails);
                SetOverwriteChanges(Context.Instance.RoutineErrorHistories);
                SetOverwriteChanges(Context.Instance.RoutineErrors);
                SetOverwriteChanges(Context.Instance.Escalations);

                //var writer = new ConsoleInFile(@"C:\Temp\SchedulerMonitorLog.txt");
                //Console.SetOut(writer);

                UpdateErrorEmail();
                SendDashboardEmail();
                CheckServices();
            }
            catch (Exception e)
            {
                //EventLog.WriteEntry(string.Concat(e.Message, " ",
                //                                  e.InnerException != null ? e.InnerException.Message : string.Empty, " ",
                //                                  e.StackTrace), EventLogEntryType.Error);
                LogUtil.PathLogErro = ErrorLog;
                LogUtil.LogError(EventLog, e);
            }
            finally
            {
                ProcessRunning = false;
            }
        }

        private static void SetOverwriteChanges<T>(ObjectSet<T> objectSet) where T : GeneralAction
        {
            try
            {
                objectSet.MergeOption = MergeOption.OverwriteChanges;
            }
            catch { }
        }

        private static void SendDashboardEmail()
        {
            var emailSchedules = new EmailSchedule().SelectAllActive<EmailSchedule>();
            
            foreach (var emailSchedule in emailSchedules)
            {
                try
                {
                    Context.Instance.LoadProperty(emailSchedule, y => y.Schedule, MergeOption.OverwriteChanges);
                    Context.Instance.LoadProperty(emailSchedule, y => y.Category, MergeOption.OverwriteChanges);
                    Context.Instance.LoadProperty(emailSchedule, y => y.User, MergeOption.OverwriteChanges);
                }
                catch { }

                if (!RoutineSchedule.RunOnDate(emailSchedule.Schedule, DateTime.Today)) return;

                if (emailSchedule.LastSent.HasValue && emailSchedule.LastSent.Value.Date != DateTime.Today
                    && emailSchedule.Schedule.Time.TimeOfDay >= DateTime.Now.TimeOfDay) continue;

                //if (emailSchedule.LastSent.HasValue && emailSchedule.LastSent.Value.Date >= DateTime.Today &&
                //    emailSchedule.Schedule.Time.TimeOfDay > DateTime.Now.TimeOfDay) continue;

                var parameters = new Dictionary<string, string>
                        {
                            {"[WebUrl]" , WebUrl },
                            {"[EmailScheduleId]",emailSchedule.Id.ToString() },
                            {"[CategoryName]", emailSchedule.Category.Name},
                            {"[Date]", DateTime.Now.ToString("dd/MM/yyyy hh:mm")},
                        };

                //FillEmail(new List<string> { emailSchedule.User.Soeid }, "DashboardScheduledMonitor", parameters);

                SendEmailWithTemplate(new List<string> { emailSchedule.User.Soeid }, null, null, 3, parameters);

                emailSchedule.LastSent = DateTime.Now;
                emailSchedule.Save(emailSchedule);
            }
        }

        private static void CheckSharedFolders()
        {
            var sharedFolders = new SharedFolder().SelectAllActive<SharedFolder>();

            foreach (var sharedFolder in sharedFolders)
            {
                var size = WindowsUtil.GetDirectorySize(sharedFolder.SharedFolder1, NetworkUser, NetWorkPassword);
            }
        }

        private static void CheckServices()
        {
            const string datePath = @"C:\Temp\LastEmailSentServicesStopped.txt";

            if (File.Exists(datePath))
            {
                //var dateString = File.ReadAllText(datePath);
                //lastEmailSentServicesStopped = !string.IsNullOrWhiteSpace(dateString) ? DateTime.Parse(dateString) : DateTime.MinValue;
                lastEmailSentServicesStopped = File.GetLastWriteTime(datePath);
            }

            //Console.WriteLine("{0} Checking Citi Scheduler Services", DateConsoleWrite);

            var machines = new Machine().SelectAllActive<Machine>();
            var machineServices = FillMachineServices(machines);
            var errors = machineServices.Select(CheckService).ToList();

            errors = errors.Where(x => x != null).ToList();

            if (!errors.Any() || !((DateTime.Now - lastEmailSentServicesStopped).TotalMinutes >= 30)) return;

            //Console.WriteLine("{0} Sending email", DateConsoleWrite);
            File.WriteAllText(datePath, DateTime.Now.ToString());

            var contentWarning = GetHtmlByTemplateId(996);
            string contentReplaced = string.Empty;

            errors.ForEach(x => contentReplaced += ReplaceContentParameters(x, contentWarning));

            var parameters = new Dictionary<string, string> { { "[MachineServices]", contentReplaced } };

            SendEmailWithTemplate(machines.Select(x => x.User.Soeid).ToList(), null, null, 995, parameters);
        }

        private static IEnumerable<MachineService> FillMachineServices(IEnumerable<Machine> machines)
        {
            var machineServices = new List<MachineService>();

            foreach (var machine in machines.Where(x => x.ExecutionCenter || x.MonitorCenter || x.SchedulerCenter))
            {
                //Console.WriteLine("{0} Machine: {1}", DateConsoleWrite, machine.HostName);

                if (machine.ExecutionCenter)
                    machineServices.Add(new MachineService(machine, ServiceExecutionName, Domain, NetworkUser, NetWorkPassword));

                if (machine.SchedulerCenter)
                    machineServices.Add(new MachineService(machine, ServiceSchedulerName, Domain, NetworkUser, NetWorkPassword));

                if (machine.MonitorCenter)
                    machineServices.Add(new MachineService(machine, ServiceMonitorName, Domain, NetworkUser, NetWorkPassword));

                //Console.WriteLine();
            }

            return machineServices;
        }

        private static string ReplaceContentParameters(MachineService machineService, string template)
        {
            var replaced = template.Replace("[MachineName]", machineService.Machine.HostName);
            replaced = replaced.Replace("[Responsible]", machineService.Responsible.Name);
            return replaced.Replace("[ServiceName]", machineService.ServiceName);
        }

        private static string GetHtmlByTemplateId(int templateId)
        {
            using (var client = new EmailClient())
            {
                return client.GetTemplateById(templateId);
            }
        }

        private static void SendEmailWithTemplate(List<string> to, List<string> cc, List<string> bcc, int templateId, Dictionary<string, string> parameters)
        {
            using (var client = new EmailClient())
            {
                to = to != null ? to.Where(x => !string.IsNullOrWhiteSpace(x)).Distinct().ToList() : new List<string>();
                cc = cc != null ? cc.Where(x => !string.IsNullOrWhiteSpace(x)).Distinct().ToList() : new List<string>();
                bcc = bcc != null ? bcc.Where(x => !string.IsNullOrWhiteSpace(x)).Distinct().ToList() : new List<string>();

                var email = new EmailSenderWcf.Email
                {
                    TemplateId = templateId,
                    Importance = EnumsEmailImportance.Normal,
                    To = to.Select(x => new EmailSenderWcf.EmailAddress { Description = x }).ToList(),
                    Cc = cc.Select(x => new EmailSenderWcf.EmailAddress { Description = x }).ToList(),
                    Bcc = bcc.Select(x => new EmailSenderWcf.EmailAddress { Description = x }).ToList(),
                    Parameters = parameters
                };

                client.InsertEmailInQueue(email);
            }
        }

        private static MachineService CheckService(MachineService machineService)
        {
            //Console.WriteLine("{0} Checking service {1} on {2}", DateConsoleWrite, machineService.ServiceName, machineService.Machine.HostName);
            var status = machineService.GetServiceStatus();

            if (status != ServiceControllerStatus.Running)
            {
                Thread.Sleep(15000);
                status = machineService.GetServiceStatus();

                if (status != ServiceControllerStatus.Running)
                {
                    try
                    {
                        machineService.StartService();
                    }
                    catch
                    {
                        return machineService;
                    }
                }
            }

            return null;
        }

        private static void UpdateErrorEmail()
        {
            var activeErrors = new RoutineError().GetActiveErrors();

            foreach (var routineError in activeErrors)
            {
                try
                {
                    Context.Instance.LoadProperty(routineError.RoutineExecution.Routine, y => y.Escalations, MergeOption.OverwriteChanges);
                }
                catch { }

                var minutes = (DateTime.Now - routineError.OcurredOn).TotalMinutes;
                var escalations = routineError.RoutineExecution.Routine.Escalations.Where(x => x.Minutes <= minutes).OrderBy(x => x.Minutes);
                var sentEmails = new RoutineErrorEmail().GetErrorByRoutineErrorId(routineError.Id);

                var escalationToSend = escalations.Where(x => sentEmails.All(y => y.EscalationId != x.Id)).ToList();

                escalationToSend.ForEach(x =>
                {
                    var errorEmail = new RoutineErrorEmail
                    {
                        EscalationId = x.Id,
                        RoutineErrorId = routineError.Id,
                        SentDate = DateTime.Now
                    };
                    errorEmail.Save(errorEmail);

                    var parameters = new Dictionary<string, string>
                        {
                            {"[WebUrl]" , WebUrl },
                            {"[RoutineExecutionId]", routineError.RoutineExecutionId.ToString() },
                            {"[RoutineError]", x.Routine.Name}
                        };

                    FillEmail(x.AllUsers.Split(';').ToList(), "EmailErrorMonitor", parameters);
                }
                );
            }
        }

        private static Domain.Email FillEmail(IList<string> users, string applicationUniqueKey, IDictionary<string, string> parameters)
        {
            var email = new Domain.Email
                {
                    ApplicationUniqueKey = Guid.NewGuid().ToString(),
                    Approved = true,
                    CreatedDate = DateTime.Now,
                    EmailData = new EmailData().SelectAllActive<EmailData>().First(y => y.ApplicationId == SchedulerApplication.Id
                        && y.ApplicationUniqueKey == applicationUniqueKey),
                };

            foreach (var emailDataParameter in email.EmailData.EmailDataParameters)
            {
                string value;
                parameters.TryGetValue(emailDataParameter.Name, out value);

                if (string.IsNullOrEmpty(value)) continue;

                email.EmailParameters.Add(new EmailParameter { ParameterId = emailDataParameter.Id, Value = value });
            }

            var address = new Domain.EmailAddress();

            foreach (var user in users)
            {
                var emailAddress = address.SelectAll<Domain.EmailAddress>().FirstOrDefault(y => y.Address == user);
                email.EmailReceivers.Add(new EmailReceiver
                    {
                        EmailAddressId = emailAddress != null ? emailAddress.Id : 0,
                        Type = users.IndexOf(user) == 0 ? (int)EmailReceiverType.To : (int)EmailReceiverType.Cc,
                        EmailAddress = emailAddress != null ? null : new Domain.EmailAddress { Address = user }
                    });
            }

            email.Save(email);
            return email;
        }
    }
}
